home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / mgr / sparcmgr / demo2.zoo / demo / ex / ex_io.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-04-24  |  16.3 KB  |  928 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.  The Berkeley software License Agreement
  4.  * specifies the terms and conditions for redistribution.
  5.  */
  6.  
  7. #ifndef lint
  8. static char *sccsid = "@(#)ex_io.c    7.13 (Berkeley) 3/9/87; 1.2 (Bellcore)    87/04/24";
  9. #endif not lint
  10.  
  11. #include "ex.h"
  12. #include "ex_argv.h"
  13. #include "ex_temp.h"
  14. #include "ex_tty.h"
  15. #include "ex_vis.h"
  16.  
  17. /*
  18.  * File input/output, source, preserve and recover
  19.  */
  20.  
  21. /*
  22.  * Following remember where . was in the previous file for return
  23.  * on file switching.
  24.  */
  25. int    altdot;
  26. int    oldadot;
  27. bool    wasalt;
  28. short    isalt;
  29.  
  30. long    cntch;            /* Count of characters on unit io */
  31. #ifndef VMUNIX
  32. short    cntln;            /* Count of lines " */
  33. #else
  34. int    cntln;
  35. #endif
  36. long    cntnull;        /* Count of nulls " */
  37. long    cntodd;            /* Count of non-ascii characters " */
  38.  
  39. /*
  40.  * Parse file name for command encoded by comm.
  41.  * If comm is E then command is doomed and we are
  42.  * parsing just so user won't have to retype the name.
  43.  */
  44. filename(comm)
  45.     int comm;
  46. {
  47.     register int c = comm, d;
  48.     register int i;
  49.  
  50.     d = ex_getchar();
  51.     if (endcmd(d)) {
  52.         if (savedfile[0] == 0 && comm != 'f')
  53.             error("No file|No current filename");
  54.         CP(file, savedfile);
  55.         wasalt = (isalt > 0) ? isalt-1 : 0;
  56.         isalt = 0;
  57.         oldadot = altdot;
  58.         if (c == 'e' || c == 'E')
  59.             altdot = lineDOT();
  60.         if (d == EOF)
  61.             ungetchar(d);
  62.     } else {
  63.         ungetchar(d);
  64.         getone();
  65.         eol();
  66.         if (savedfile[0] == 0 && c != 'E' && c != 'e') {
  67.             c = 'e';
  68.             edited = 0;
  69.         }
  70.         wasalt = strcmp(file, altfile) == 0;
  71.         oldadot = altdot;
  72.         switch (c) {
  73.  
  74.         case 'f':
  75.             edited = 0;
  76.             /* fall into ... */
  77.  
  78.         case 'e':
  79.             if (savedfile[0]) {
  80.                 altdot = lineDOT();
  81.                 CP(altfile, savedfile);
  82.             }
  83.             CP(savedfile, file);
  84.             break;
  85.  
  86.         default:
  87.             if (file[0]) {
  88.                 if (c != 'E')
  89.                     altdot = lineDOT();
  90.                 CP(altfile, file);
  91.             }
  92.             break;
  93.         }
  94.     }
  95.     if (hush && comm != 'f' || comm == 'E')
  96.         return;
  97.     if (file[0] != 0) {
  98.         lprintf("\"%s\"", file);
  99.         if (comm == 'f') {
  100.             if (value(READONLY))
  101.                 ex_printf(" [Read only]");
  102.             if (!edited)
  103.                 ex_printf(" [Not edited]");
  104.             if (tchng)
  105.                 ex_printf(" [Modified]");
  106.         }
  107.         flush();
  108.     } else
  109.         ex_printf("No file ");
  110.     if (comm == 'f') {
  111.         if (!(i = lineDOL()))
  112.             i++;
  113.         ex_printf(" line %d of %d --%ld%%--", lineDOT(), lineDOL(),
  114.             (long) 100 * lineDOT() / i);
  115.     }
  116. }
  117.  
  118. /*
  119.  * Get the argument words for a command into genbuf
  120.  * expanding # and %.
  121.  */
  122. getargs()
  123. {
  124.     register int c;
  125.     register char *cp, *fp;
  126.     static char fpatbuf[32];    /* hence limit on :next +/pat */
  127.  
  128.     pastwh();
  129.     if (peekchar() == '+') {
  130.         for (cp = fpatbuf;;) {
  131.             c = *cp++ = ex_getchar();
  132.             if (cp >= &fpatbuf[sizeof(fpatbuf)])
  133.                 error("Pattern too long");
  134.             if (c == '\\' && isspace(peekchar()))
  135.                 c = ex_getchar();
  136.             if (c == EOF || isspace(c)) {
  137.                 ungetchar(c);
  138.                 *--cp = 0;
  139.                 firstpat = &fpatbuf[1];
  140.                 break;
  141.             }
  142.         }
  143.     }
  144.     if (skipend())
  145.         return (0);
  146.     CP(genbuf, "echo "); cp = &genbuf[5];
  147.     for (;;) {
  148.         c = ex_getchar();
  149.         if (endcmd(c)) {
  150.             ungetchar(c);
  151.             break;
  152.         }
  153.         switch (c) {
  154.  
  155.         case '\\':
  156.             if (any(peekchar(), "#%|"))
  157.                 c = ex_getchar();
  158.             /* fall into... */
  159.  
  160.         default:
  161.             if (cp > &genbuf[LBSIZE - 2])
  162. flong:
  163.                 error("Argument buffer overflow");
  164.             *cp++ = c;
  165.             break;
  166.  
  167.         case '#':
  168.             fp = altfile;
  169.             if (*fp == 0)
  170.                 error("No alternate filename@to substitute for #");
  171.             goto filexp;
  172.  
  173.         case '%':
  174.             fp = savedfile;
  175.             if (*fp == 0)
  176.                 error("No current filename@to substitute for %%");
  177. filexp:
  178.             while (*fp) {
  179.                 if (cp > &genbuf[LBSIZE - 2])
  180.                     goto flong;
  181.                 *cp++ = *fp++;
  182.             }
  183.             break;
  184.         }
  185.     }
  186.     *cp = 0;
  187.     return (1);
  188. }
  189.  
  190. /*
  191.  * Glob the argument words in genbuf, or if no globbing
  192.  * is implied, just split them up directly.
  193.  */
  194. glob(gp)
  195.     struct glob *gp;
  196. {
  197.     int pvec[2];
  198.     register char **argv = gp->argv;
  199.     register char *cp = gp->argspac;
  200.     register int c;
  201.     char ch;
  202.     int nleft = NCARGS;
  203.  
  204.     gp->argc0 = 0;
  205.     if (gscan() == 0) {
  206.         register char *v = genbuf + 5;        /* strlen("echo ") */
  207.  
  208.         for (;;) {
  209.             while (isspace(*v))
  210.                 v++;
  211.             if (!*v)
  212.                 break;
  213.             *argv++ = cp;
  214.             while (*v && !isspace(*v))
  215.                 *cp++ = *v++;
  216.             *cp++ = 0;
  217.             gp->argc0++;
  218.         }
  219.         *argv = 0;
  220.         return;
  221.     }
  222.     if (pipe(pvec) < 0)
  223.         error("Can't make pipe to glob");
  224.     pid = vfork();
  225.     io = pvec[0];
  226.     if (pid < 0) {
  227.         close(pvec[1]);
  228.         error("Can't fork to do glob");
  229.     }
  230.     if (pid == 0) {
  231.         int oerrno;
  232.  
  233.         close(1);
  234.         dup(pvec[1]);
  235.         close(pvec[0]);
  236.         close(2);    /* so errors don't mess up the screen */
  237.         ignore(open("/dev/null", 1));
  238.         execl(svalue(SHELL), "sh", "-c", genbuf, 0);
  239.         oerrno = errno;
  240.         close(1);
  241.         dup(2);
  242.         errno = oerrno;
  243.         filioerr(svalue(SHELL));
  244.     }
  245.     close(pvec[1]);
  246.     do {
  247.         *argv = cp;
  248.         for (;;) {
  249.             if (read(io, &ch, 1) != 1) {
  250.                 close(io);
  251.                 c = -1;
  252.             } else
  253.                 c = ch & TRIM;
  254.             if (c <= 0 || isspace(c))
  255.                 break;
  256.             *cp++ = c;
  257.             if (--nleft <= 0)
  258.                 error("Arg list too long");
  259.         }
  260.         if (cp != *argv) {
  261.             --nleft;
  262.             *cp++ = 0;
  263.             gp->argc0++;
  264.             if (gp->argc0 >= NARGS)
  265.                 error("Arg list too long");
  266.             argv++;
  267.         }
  268.     } while (c >= 0);
  269.     waitfor();
  270.     if (gp->argc0 == 0)
  271.         error("No match");
  272. }
  273.  
  274. /*
  275.  * Scan genbuf for shell metacharacters.
  276.  * Set is union of v7 shell and csh metas.
  277.  */
  278. gscan()
  279. {
  280. #ifndef    vms
  281.     register char *cp;
  282.  
  283.     for (cp = genbuf; *cp; cp++)
  284.         if (any(*cp, "~{[*?$`'\"\\"))
  285.         if (any(*cp, "~{[*?$`'\"\\"))
  286.             return (1);
  287.     return (0);
  288. #else
  289.     return 0;    /* Never have meta-characters in vms */
  290. #endif
  291. }
  292.  
  293. /*
  294.  * Parse one filename into file.
  295.  */
  296. struct glob G;
  297. getone()
  298. {
  299.     register char *str;
  300.  
  301.     if (getargs() == 0)
  302.         error("Missing filename");
  303.     glob(&G);
  304.     if (G.argc0 > 1)
  305.         error("Ambiguous|Too many file names");
  306.     str = G.argv[G.argc0 - 1];
  307.     if (strlen(str) > FNSIZE - 4)
  308.         error("Filename too long");
  309.     CP(file, str);
  310. }
  311.  
  312. /*
  313.  * Read a file from the world.
  314.  * C is command, 'e' if this really an edit (or a recover).
  315.  */
  316. rop(c)
  317.     int c;
  318. {
  319.     register int i;
  320.     struct stat stbuf;
  321.     short magic;
  322.     static int ovro;    /* old value(READONLY) */
  323.     static int denied;    /* 1 if READONLY was set due to file permissions */
  324.  
  325.     io = open(file, 0);
  326.     if (io < 0) {
  327.         if (c == 'e' && errno == ENOENT) {
  328.             edited++;
  329.             /*
  330.              * If the user just did "ex foo" he is probably
  331.              * creating a new file.  Don't be an error, since
  332.              * this is ugly, and it screws up the + option.
  333.              */
  334.             if (!seenprompt) {
  335.                 ex_printf(" [New file]");
  336.                 noonl();
  337.                 return;
  338.             }
  339.         }
  340.         syserror();
  341.     }
  342.     if (fstat(io, &stbuf))
  343.         syserror();
  344.     switch (stbuf.st_mode & S_IFMT) {
  345.  
  346.     case S_IFBLK:
  347.         error(" Block special file");
  348.  
  349.     case S_IFCHR:
  350.         if (isatty(io))
  351.             error(" Teletype");
  352.         if (samei(&stbuf, "/dev/null"))
  353.             break;
  354.         error(" Character special file");
  355.  
  356.     case S_IFDIR:
  357.         error(" Directory");
  358.  
  359.     case S_IFREG:
  360. #ifdef CRYPT
  361.         if (xflag)
  362.             break;
  363. #endif
  364.         i = read(io, (char *) &magic, sizeof(magic));
  365.         lseek(io, 0l, 0);
  366.         if (i != sizeof(magic))
  367.             break;
  368. #ifndef vms
  369.         switch (magic) {
  370.  
  371.         case 0405:    /* data overlay on exec */
  372.         case 0407:    /* unshared */
  373.         case 0410:    /* shared text */
  374.         case 0411:    /* separate I/D */
  375.         case 0413:    /* VM/Unix demand paged */
  376.         case 0430:    /* PDP-11 Overlay shared */
  377.         case 0431:    /* PDP-11 Overlay sep I/D */
  378.             error(" Executable");
  379.  
  380.         /*
  381.          * We do not forbid the editing of portable archives
  382.          * because it is reasonable to edit them, especially
  383.          * if they are archives of text files.  This is
  384.          * especially useful if you archive source files together
  385.          * and copy them to another system with ~%take, since
  386.          * the files sometimes show up munged and must be fixed.
  387.          */
  388.         case 0177545:
  389.         case 0177555:
  390.             error(" Archive");
  391.  
  392.         default:
  393. #ifdef mbb
  394.             /* C/70 has a 10 bit byte */
  395.             if (magic & 03401600)
  396. #else
  397.             /* Everybody else has an 8 bit byte */
  398.             if (magic & 0100200)
  399. #endif
  400.                 error(" Non-ascii file");
  401.             break;
  402.         }
  403. #endif
  404.     }
  405.     if (c != 'r') {
  406.         if (value(READONLY) && denied) {
  407.             value(READONLY) = ovro;
  408.             denied = 0;
  409.         }
  410.         if ((stbuf.st_mode & 0222) == 0 || access(file, 2) < 0) {
  411.             ovro = value(READONLY);
  412.             denied = 1;
  413.             value(READONLY) = 1;
  414.         }
  415.     }
  416.     if (value(READONLY)) {
  417.         ex_printf(" [Read only]");
  418.         flush();
  419.     }
  420.     if (c == 'r')
  421.         setdot();
  422.     else
  423.         setall();
  424.     if (FIXUNDO && inopen && c == 'r')
  425.         undap1 = undap2 = dot + 1;
  426.     rop2();
  427.     rop3(c);
  428. }
  429.  
  430. rop2()
  431. {
  432.     line *first, *last, *a;
  433.     struct stat statb;
  434.  
  435.     deletenone();
  436.     clrstats();
  437.     first = addr2 + 1;
  438.     if (fstat(io, &statb) < 0)
  439.         bsize = LBSIZE;
  440.     else {
  441.         bsize = statb.st_blksize;
  442.         if (bsize <= 0)
  443.             bsize = LBSIZE;
  444.     }
  445.     ignore(append(getfile, addr2));
  446.     last = dot;
  447.     /*
  448.      *    if the modeline variable is set,
  449.      *    check the first and last five lines of the file
  450.      *    for a mode line.
  451.      */
  452.     if (value(MODELINE)) {
  453.         for (a=first; a<=last; a++) {
  454.             if (a==first+5 && last-first > 10)
  455.                 a = last - 4;
  456.             getline(*a);
  457.             checkmodeline(linebuf);
  458.         }
  459.     }
  460. }
  461.  
  462. rop3(c)
  463.     int c;
  464. {
  465.  
  466.     if (iostats() == 0 && c == 'e')
  467.         edited++;
  468.     if (c == 'e') {
  469.         if (wasalt || firstpat) {
  470.             register line *addr = zero + oldadot;
  471.  
  472.             if (addr > dol)
  473.                 addr = dol;
  474.             if (firstpat) {
  475.                 globp = (*firstpat) ? firstpat : "$";
  476.                 commands(1,1);
  477.                 firstpat = 0;
  478.             } else if (addr >= one) {
  479.                 if (inopen)
  480.                     dot = addr;
  481.                 markpr(addr);
  482.             } else
  483.                 goto other;
  484.         } else
  485. other:
  486.             if (dol > zero) {
  487.                 if (inopen)
  488.                     dot = one;
  489.                 markpr(one);
  490.             }
  491.         if(FIXUNDO)
  492.             undkind = UNDNONE;
  493.         if (inopen) {
  494.             vcline = 0;
  495.             vreplace(0, LINES, lineDOL());
  496.         }
  497.     }
  498. }
  499.  
  500. /*
  501.  * Are these two really the same inode?
  502.  */
  503. samei(sp, cp)
  504.     struct stat *sp;
  505.     char *cp;
  506. {
  507.     struct stat stb;
  508.  
  509.     if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
  510.         return (0);
  511.     return (sp->st_ino == stb.st_ino);
  512. }
  513.  
  514. /* Returns from edited() */
  515. #define    EDF    0        /* Edited file */
  516. #define    NOTEDF    -1        /* Not edited file */
  517. #define    PARTBUF    1        /* Write of partial buffer to Edited file */
  518.  
  519. /*
  520.  * Write a file.
  521.  */
  522. wop(dofname)
  523. bool dofname;    /* if 1 call filename, else use savedfile */
  524. {
  525.     register int c, exclam, nonexist;
  526.     line *saddr1, *saddr2;
  527.     struct stat stbuf;
  528.  
  529.     c = 0;
  530.     exclam = 0;
  531.     if (dofname) {
  532.         if (peekchar() == '!')
  533.             exclam++, ignchar();
  534.         ignore(skipwh());
  535.         while (peekchar() == '>')
  536.             ignchar(), c++, ignore(skipwh());
  537.         if (c != 0 && c != 2)
  538.             error("Write forms are 'w' and 'w>>'");
  539.         filename('w');
  540.     } else {
  541.         if (savedfile[0] == 0)
  542.             error("No file|No current filename");
  543.         saddr1=addr1;
  544.         saddr2=addr2;
  545.         addr1=one;
  546.         addr2=dol;
  547.         CP(file, savedfile);
  548.         if (inopen) {
  549.             vclrech(0);
  550.             splitw++;
  551.         }
  552.         lprintf("\"%s\"", file);
  553.     }
  554.     nonexist = stat(file, &stbuf);
  555.     switch (c) {
  556.  
  557.     case 0:
  558.         if (!exclam && (!value(WRITEANY) || value(READONLY)))
  559.         switch (edfile()) {
  560.         
  561.         case NOTEDF:
  562.             if (nonexist)
  563.                 break;
  564.             if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
  565.                 if (samei(&stbuf, "/dev/null"))
  566.                     break;
  567.                 if (samei(&stbuf, "/dev/tty"))
  568.                     break;
  569.             }
  570.             io = open(file, 1);
  571.             if (io < 0)
  572.                 syserror();
  573.             if (!isatty(io))
  574.                 serror(" File exists| File exists - use \"w! %s\" to overwrite", file);
  575.             close(io);
  576.             break;
  577.  
  578.         case EDF:
  579.             if (value(READONLY))
  580.                 error(" File is read only");
  581.             break;
  582.  
  583.         case PARTBUF:
  584.             if (value(READONLY))
  585.                 error(" File is read only");
  586.             error(" Use \"w!\" to write partial buffer");
  587.         }
  588. cre:
  589. /*
  590.         synctmp();
  591. */
  592. #ifdef V6
  593.         io = creat(file, 0644);
  594. #else
  595.         io = creat(file, 0666);
  596. #endif
  597.         if (io < 0)
  598.             syserror();
  599.         writing = 1;
  600.         if (hush == 0)
  601.             if (nonexist)
  602.                 ex_printf(" [New file]");
  603.             else if (value(WRITEANY) && edfile() != EDF)
  604.                 ex_printf(" [Existing file]");
  605.         break;
  606.  
  607.     case 2:
  608.         io = open(file, 1);
  609.         if (io < 0) {
  610.             if (exclam || value(WRITEANY))
  611.                 goto cre;
  612.             syserror();
  613.         }
  614.         lseek(io, 0l, 2);
  615.         break;
  616.     }
  617.     putfile(0);
  618. #ifndef    vms
  619.     (void) fsync(io);
  620. #endif
  621.     ignore(iostats());
  622.     if (c != 2 && addr1 == one && addr2 == dol) {
  623.         if (eq(file, savedfile))
  624.             edited = 1;
  625.         ex_sync();
  626.     }
  627.     if (!dofname) {
  628.         addr1 = saddr1;
  629.         addr2 = saddr2;
  630.     }
  631.     writing = 0;
  632. }
  633.  
  634. /*
  635.  * Is file the edited file?
  636.  * Work here is that it is not considered edited
  637.  * if this is a partial buffer, and distinguish
  638.  * all cases.
  639.  */
  640. edfile()
  641. {
  642.  
  643.     if (!edited || !eq(file, savedfile))
  644.         return (NOTEDF);
  645.     return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
  646. }
  647.  
  648. /*
  649.  * Extract the next line from the io stream.
  650.  */
  651. char *nextip;
  652.  
  653. getfile()
  654. {
  655.     register short c;
  656.     register char *lp, *fp;
  657.  
  658.     lp = linebuf;
  659.     fp = nextip;
  660.     do {
  661.         if (--ninbuf < 0) {
  662.             ninbuf = read(io, genbuf, (int) bsize) - 1;
  663.             if (ninbuf < 0) {
  664.                 if (lp != linebuf) {
  665.                     lp++;
  666.                     ex_printf(" [Incomplete last line]");
  667.                     break;
  668.                 }
  669.                 return (EOF);
  670.             }
  671. #ifdef CRYPT
  672.             if (kflag) {
  673.                 fp = genbuf;
  674.                 while(fp < &genbuf[ninbuf]) {
  675.                     if (*fp++ & 0200) {
  676.                         crblock(perm, genbuf, ninbuf+1,
  677.     cntch);
  678.                         break;
  679.                     }
  680.                 }
  681.             }
  682. #endif
  683.             fp = genbuf;
  684.             cntch += ninbuf+1;
  685.         }
  686.         if (lp >= &linebuf[LBSIZE]) {
  687.             error(" Line too long");
  688.         }
  689.         c = *fp++;
  690.         if (c == 0) {
  691.             cntnull++;
  692.             continue;
  693.         }
  694.         if (c & QUOTE) {
  695.             cntodd++;
  696.             c &= TRIM;
  697.             if (c == 0)
  698.                 continue;
  699.         }
  700.         *lp++ = c;
  701.     } while (c != '\n');
  702.     *--lp = 0;
  703.     nextip = fp;
  704.     cntln++;
  705.     return (0);
  706. }
  707.  
  708. /*
  709.  * Write a range onto the io stream.
  710.  */
  711. /* ARGSUSED */
  712. putfile(isfilter)
  713. int isfilter;
  714. {
  715.     line *a1;
  716.     register char *fp, *lp;
  717.     register int nib;
  718.     struct stat statb;
  719.  
  720.     a1 = addr1;
  721.     clrstats();
  722.     cntln = addr2 - a1 + 1;
  723.     if (cntln == 0)
  724.         return;
  725.     if (fstat(io, &statb) < 0)
  726.         bsize = LBSIZE;
  727.     else {
  728.         bsize = statb.st_blksize;
  729.         if (bsize <= 0)
  730.             bsize = LBSIZE;
  731.     }
  732.     nib = bsize;
  733.     fp = genbuf;
  734.     do {
  735.         getline(*a1++);
  736.         lp = linebuf;
  737.         for (;;) {
  738.             if (--nib < 0) {
  739.                 nib = fp - genbuf;
  740. #ifdef CRYPT
  741.                         if(kflag && !isfilter)
  742.                                         crblock(perm, genbuf, nib, cntch);
  743. #endif
  744.                 if (write(io, genbuf, nib) != nib) {
  745.                     wrerror();
  746.                 }
  747.                 cntch += nib;
  748.                 nib = bsize - 1;
  749.                 fp = genbuf;
  750.             }
  751.             if ((*fp++ = *lp++) == 0) {
  752.                 fp[-1] = '\n';
  753.                 break;
  754.             }
  755.         }
  756.     } while (a1 <= addr2);
  757.     nib = fp - genbuf;
  758. #ifdef CRYPT
  759.     if(kflag && !isfilter)
  760.         crblock(perm, genbuf, nib, cntch);
  761. #endif
  762.     if (write(io, genbuf, nib) != nib) {
  763.         wrerror();
  764.     }
  765.     cntch += nib;
  766. }
  767.  
  768. /*
  769.  * A write error has occurred;  if the file being written was
  770.  * the edited file then we consider it to have changed since it is
  771.  * now likely scrambled.
  772.  */
  773. wrerror()
  774. {
  775.  
  776.     if (eq(file, savedfile) && edited)
  777.         change();
  778.     syserror();
  779. }
  780.  
  781. /*
  782.  * Source command, handles nested sources.
  783.  * Traps errors since it mungs unit 0 during the source.
  784.  */
  785. short slevel;
  786. short ttyindes;
  787.  
  788. source(fil, okfail)
  789.     char *fil;
  790.     bool okfail;
  791. {
  792.     jmp_buf osetexit;
  793.     register int saveinp, ointty, oerrno;
  794.     char *saveglobp;
  795.     short savepeekc;
  796.  
  797.     signal(SIGINT, SIG_IGN);
  798.     saveinp = dup(0);
  799.     savepeekc = peekc;
  800.     saveglobp = globp;
  801.     peekc = 0; globp = 0;
  802.     if (saveinp < 0)
  803.         error("Too many nested sources");
  804.     if (slevel <= 0)
  805.         ttyindes = saveinp;
  806.     close(0);
  807.     if (open(fil, 0) < 0) {
  808.         oerrno = errno;
  809.         setrupt();
  810.         dup(saveinp);
  811.         close(saveinp);
  812.         errno = oerrno;
  813.         if (!okfail)
  814.             filioerr(fil);
  815.         return;
  816.     }
  817.     slevel++;
  818.     ointty = intty;
  819.     intty = isatty(0);
  820.     oprompt = value(PROMPT);
  821.     value(PROMPT) &= intty;
  822.     getexit(osetexit);
  823.     setrupt();
  824.     if (setexit() == 0)
  825.         commands(1, 1);
  826.     else if (slevel > 1) {
  827.         close(0);
  828.         dup(saveinp);
  829.         close(saveinp);
  830.         slevel--;
  831.         resexit(osetexit);
  832.         reset();
  833.     }
  834.     intty = ointty;
  835.     value(PROMPT) = oprompt;
  836.     close(0);
  837.     dup(saveinp);
  838.     close(saveinp);
  839.     globp = saveglobp;
  840.     peekc = savepeekc;
  841.     slevel--;
  842.     resexit(osetexit);
  843. }
  844.  
  845. /*
  846.  * Clear io statistics before a read or write.
  847.  */
  848. clrstats()
  849. {
  850.  
  851.     ninbuf = 0;
  852.     cntch = 0;
  853.     cntln = 0;
  854.     cntnull = 0;
  855.     cntodd = 0;
  856. }
  857.  
  858. /*
  859.  * Io is finished, close the unit and print statistics.
  860.  */
  861. iostats()
  862. {
  863.  
  864.     close(io);
  865.     io = -1;
  866.     if (hush == 0) {
  867.         if (value(TERSE))
  868.             ex_printf(" %d/%D", cntln, cntch);
  869.         else
  870.             ex_printf(" %d line%s, %D character%s", cntln, plural((long) cntln),
  871.                 cntch, plural(cntch));
  872.         if (cntnull || cntodd) {
  873.             ex_printf(" (");
  874.             if (cntnull) {
  875.                 ex_printf("%D null", cntnull);
  876.                 if (cntodd)
  877.                     ex_printf(", ");
  878.             }
  879.             if (cntodd)
  880.                 ex_printf("%D non-ASCII", cntodd);
  881.             ex_putchar(')');
  882.         }
  883.         noonl();
  884.         flush();
  885.     }
  886.     return (cntnull != 0 || cntodd != 0);
  887. }
  888.  
  889. #ifdef USG
  890. # define index strchr
  891. # define rindex strrchr
  892. #endif
  893. #ifdef USG3TTY
  894. # define index strchr
  895. # define rindex strrchr
  896. #endif
  897. #ifdef vms
  898. # define index strchr
  899. # define rindex strrchr
  900. #endif
  901.  
  902. checkmodeline(line)
  903. char *line;
  904. {
  905.     char *beg, *end;
  906.     char cmdbuf[1024];
  907.     char *index(), *rindex(), *strncpy();
  908.  
  909.     beg = index(line, ':');
  910.     if (beg == NULL)
  911.         return;
  912.     if (&beg[-3] < line)
  913.         return;
  914.     if (!(  ( (beg[-3] == ' ' || beg[-3] == '\t')
  915.             && beg[-2] == 'e'
  916.         && beg[-1] == 'x')
  917.          || ( (beg[-3] == ' ' || beg[-3] == '\t')
  918.             && beg[-2] == 'v'
  919.         && beg[-1] == 'i'))) return;
  920.     strncpy(cmdbuf, beg+1, sizeof cmdbuf);
  921.     end = rindex(cmdbuf, ':');
  922.     if (end == NULL)
  923.         return;
  924.     *end = 0;
  925.     globp = cmdbuf;
  926.     commands(1, 1);
  927. }
  928.